﻿// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动！任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任！

using Sinc.ERP.Application.Service.ChineMobile;
using Sinc.ERP.Core.Enum;

namespace Sinc.ERP.Application;

/// <summary>
/// 实体卡服务 🧩
/// </summary>
[ApiDescriptionSettings(ApplicationConst.GroupName, Order = 100)]
public class ProductCardService : CustomBaseService, IDynamicApiController
{
    private readonly SqlSugarRepository<ProductCard> _productCardRep;
    private readonly ISqlSugarClient _sqlSugarClient;
    private readonly SysDictTypeService _sysDictTypeService;
    private readonly IEventPublisher _eventPublisher;
    private readonly UserManager _userManager;
    private readonly SysMenuService _sysMenuService;

    public ProductCardService(SqlSugarRepository<ProductCard> productCardRep, ISqlSugarClient sqlSugarClient, SysDictTypeService sysDictTypeService
        , UserManager userManager, IEventPublisher eventPublisher, SysMenuService sysMenuService)
    {
        this._productCardRep = productCardRep;
        this._sqlSugarClient = sqlSugarClient;
        this._sysDictTypeService = sysDictTypeService;
        this._eventPublisher = eventPublisher;
        this._userManager = userManager;
        this._sysMenuService = sysMenuService;
    }

    #region 分页查询实体卡
    /// <summary>
    /// 分页查询实体卡 🔖
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("分页查询实体卡")]
    [ApiDescriptionSettings(Name = "Page"), HttpPost]
    public async Task<SqlSugarPagedList<ProductCardOutput>> Page(PageProductCardInput input)
    {
        input.Keyword = input.Keyword?.Trim();
        var query = _productCardRep.AsQueryable()
            .WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => u.BatchNumber.Contains(input.Keyword) || u.Code.Contains(input.Keyword) || u.Passcode.Contains(input.Keyword))
            .WhereIF(!string.IsNullOrWhiteSpace(input.BatchNumber), u => u.BatchNumber.Contains(input.BatchNumber.Trim()))
            .WhereIF(!string.IsNullOrWhiteSpace(input.Code), u => u.FullCode.Contains(input.Code.Trim()))
            .WhereIF(!string.IsNullOrWhiteSpace(input.Passcode), u => u.Passcode.Contains(input.Passcode.Trim()))
            .WhereIF(input.ProductId != null, u => u.ProductId == input.ProductId)
            .WhereIF(input.ExpirationDate.HasValue, u => u.ExpirationDate == input.ExpirationDate.Value)
            .WhereIF(input.Status != null, u => u.Status == input.Status)
            .WhereIF(input.CustomerId != null, u => u.OrgId == input.CustomerId)
            .Select<ProductCardOutput>();
        var modelList = await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);

        var customerList = this.CustomerService.DropdownData().Result;
        var productList = this.ProductService.DropdownData().Result;
        foreach (var model in modelList.Items)
        {
            if (!input.IncludePasscode)
                model.Passcode = string.Empty;

            var customer = customerList.FirstOrDefault(x => x.Id == model.CustomerId);
            model.CustomerLabel = customer?.Name ?? (model.CustomerId > 0 ? model.CustomerId.ToString() : string.Empty);

            var product = productList.FirstOrDefault(x => x.Id == model.ProductId);
            model.ProductLabel = product?.Name ?? model.ProductId.ToString();
        }
        return modelList;
    }
    #endregion

    #region 获取实体卡详情
    /// <summary>
    /// 获取实体卡详情 ℹ️
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("获取实体卡详情")]
    [ApiDescriptionSettings(Name = "Detail"), HttpGet]
    public async Task<ProductCard> Detail([FromQuery] QueryByIdProductCardInput input)
    {
        return await _productCardRep.GetFirstAsync(u => u.Id == input.Id);
    }
    #endregion

    #region 增加实体卡
    private static object _locker = new object();
    /// <summary>
    /// 增加实体卡 ➕
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("增加实体卡")]
    [ApiDescriptionSettings(Name = "Add"), HttpPost]
    public long Add(AddProductCardInput input)
    {
        lock (_locker)
        {
            var modelList = input.Convert();
            var batchNumber = this.SequenceInternalService.GetNextSequenceValue(ApplicationConst.Sequence_ProductCardImportBatchNumber);
            modelList.ForEach(x =>
            {
                x.BatchNumber = batchNumber;
                x.CreateUserId = this._userManager.UserId;
                x.CreateUserName = this._userManager.RealName;
                x.CreateTime = DateTime.Now;
            });
            this._sqlSugarClient.Fastest<ProductCard>().BulkCopy(modelList);
        }
        return 0;
    }
    #endregion

    #region 补充验证码
    /// <summary>
    /// 补充验证码
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("补充验证码")]
    [ApiDescriptionSettings(Name = "FillIn"), HttpPost]
    public async Task<FillInProductCardOutput> FillIn(FillInProductCardInput input)
    {
        var dict = input.Convert();
        if (dict.Count == 0)
            return new FillInProductCardOutput();

        if (input.IsBatch)
            return await this.FillIn(dict);

        // 发布到事件总线异步处理
        await _eventPublisher.PublishAsync(BusinessEventTypeEnum.ProductCard_FillIn, dict);
        return new FillInProductCardOutput();
    }

    /// <summary>
    /// 补充验证码
    /// </summary>
    /// <param name="dict"></param>
    /// <returns></returns>
    [NonAction]
    public async Task<FillInProductCardOutput> FillIn(Dictionary<string, string> dict)
    {
        var result = new FillInProductCardOutput();
        if (dict.IsEmpty())
            return result;

        var modelList = new List<ProductCard>();
        foreach (var keyValue in dict)
        {
            var code = keyValue.Key.SubstringProductCode();
            var model = await this._productCardRep.GetFirstAsync(x => x.Code == code);
            if (model == null)
            {
                result.FailureCount++;
                await this.BusinessLogService.Create($"补充验证码失败，ICCID无效：{keyValue.Key}");
                continue;
            }
            modelList.Add(model);
            if (model.Status != ProductCardStatusEnum.Created)
            {
                result.FailureCount++;
                await this.BusinessLogService.Create($"补充验证码失败，ICCID状态不允许补充：{keyValue.Key}");
                continue;
            }
            model.FullCode = keyValue.Key;
            model.Passcode = keyValue.Value;
            model.Status = ProductCardStatusEnum.Available;

            #region 生成二维码图片
            if (App.GetConfig<bool>("ProductCard:GenerateQRCode"))
            {
                var configModel = await this._sqlSugarClient.Queryable<SysConfig>().Where(x => x.Code == ApplicationConst.Config_ProductCardActivateUrlForamt).FirstAsync();
                if (!string.IsNullOrWhiteSpace(configModel?.Value))
                {
                    var url = string.Format(configModel.Value, model.FullCode, model.Passcode);
                    var fileName = $"productcard/qrcode/{DateTime.Now:yyyy}/{DateTime.Now:MM}/{DateTime.Now:dd}/{model.BatchNumber}/{model.Code}.png";
                    var pathRoot = App.GetConfig<string>("ProductCard:QRCodePathRoot");
                    if (pathRoot.IsEmpty())
                        pathRoot = App.WebHostEnvironment.WebRootPath;
                    if (!Path.IsPathRooted(pathRoot))
                        pathRoot = Path.Combine(App.WebHostEnvironment.WebRootPath, pathRoot);
                    var filePath = Path.Combine(pathRoot, fileName);
                    QRCoderExtension.Generate(url, filePath);
                    model.QRCodeUrl = fileName;
                }
            }
            #endregion

            await this._productCardRep.AsUpdateable(model).UpdateColumns(x => new { x.FullCode, x.Passcode, x.QRCodeUrl, x.Status, x.UpdateUserId, x.UpdateUserName, x.UpdateTime }).ExecuteCommandAsync();
            await this.BusinessLogService.Create($"补充验证码成功，ICCID：{keyValue.Key}");
            result.SuccessCount++;
        }

        // 将中间遗漏的ICCID查出来，如果未扫描需要提示出来
        var minCode = dict.Keys.Min().SubstringProductCode();
        var maxCode = dict.Keys.Max().SubstringProductCode();
        if (minCode != maxCode && minCode.Length == maxCode.Length && decimal.TryParse(minCode, out var startNumber) && decimal.TryParse(maxCode, out var endNumber))
        {
            do
            {
                var iccidCode = startNumber.ToString();
                var model = modelList.FirstOrDefault(x => x.Code == iccidCode) ?? await this._productCardRep.GetFirstAsync(x => x.Code == iccidCode);
                if (model.Status == ProductCardStatusEnum.Created)
                    result.MissCodeList.Add(iccidCode);
                startNumber++;
            } while (startNumber < endNumber);

            if (result.MissCodeList.IsNotEmpty())
            {
                await this.BusinessLogService.Create($"补充验证码结束，可能漏补：{string.Join(",", result.MissCodeList)}");
            }
        }
        return result;
    }
    #endregion

    #region 调拨实体卡
    /// <summary>
    /// 调拨实体卡
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("调拨实体卡")]
    [ApiDescriptionSettings(Name = "Transfer"), HttpPost, UnitOfWork]
    public async Task Transfer(TransferProductCardInput input)
    {
        var numberList = input.GetNumberList();
        if (numberList.Count > 0)
            input.Qty = numberList.Count;
        if (input.Qty == 0)
            throw Oops.Oh("调拨数量和号码段至少填一项");

        var modelList = await this._productCardRep.AsQueryable()
                                .WhereIF(numberList.Count > 0, x => numberList.Contains(x.Code))
                                .WhereIF(numberList.Count == 0, x => x.Status != ProductCardStatusEnum.Used && x.ProductId == input.ProductId.Value)
                                .WhereIF(!this._userManager.SuperAdmin && !this._userManager.SysAdmin, x => x.OrgId == this._userManager.OrgId)
                                .Take(input.Qty).ToListAsync();
        if (modelList.Count < input.Qty)
            throw Oops.Oh($"可调拨实体卡数量不够：{modelList.Count}");

        if (modelList.Any(x => x.Status == ProductCardStatusEnum.Used))
            throw Oops.Oh("不能调拨已使用状态的卡");

        var fromCustomerIdList = modelList.Select(x => x.OrgId).Distinct().ToList();
        if (fromCustomerIdList.Count > 1)
            throw Oops.Oh("调拨的实体卡不属于同一客户");

        modelList.ForEach(x =>
        {
            x.CustomerId = input.ToCustomerId.Value;
            x.OrgId = input.ToCustomerId.Value;
        });

        var transferOrder = new TransferOrder
        {
            Code = this.SequenceInternalService.GetNextSequenceValue(ApplicationConst.Sequence_ProductCardTransferOrderNumber),
            ProductId = modelList.First().ProductId,
            Qty = input.Qty,
            FromCustomerId = fromCustomerIdList.First(),
            ToCustomerId = input.ToCustomerId.Value,
            ProductList = modelList.Select(x => new TransferOrderProduct { Code = x.Code, ProductId = x.ProductId }).ToList(),
        };
        if (transferOrder.FromCustomerId == transferOrder.ToCustomerId)
            throw Oops.Oh("调拨的客户不能相同");

        var customerList = await this.CustomerService.DropdownData();
        var fromCustomer = customerList.FirstOrDefault(x => x.Id == transferOrder.FromCustomerId);
        var toCustomer = customerList.FirstOrDefault(x => x.Id == transferOrder.ToCustomerId);
        if (fromCustomer == null)
            throw Oops.Oh($"调拨的来源客户不存在：{transferOrder.FromCustomerId}");
        if (toCustomer == null)
            throw Oops.Oh($"调拨的目标客户不存在：{transferOrder.ToCustomerId}");

        await this._productCardRep.AsUpdateable(modelList).UpdateColumns(x => new { x.CustomerId, x.OrgId, x.UpdateUserId, x.UpdateUserName, x.UpdateTime }).ExecuteCommandAsync();
        await this.TransferOrderService.Add(transferOrder);

        var codes = string.Join(",", modelList.Select(x => x.Code));
        await this.BusinessLogService.Create($"调拨实体卡，来源：{fromCustomer.Name}，目标客户：{toCustomer.Name}，ICCID：{codes}");
    }
    #endregion

    #region 更新实体卡
    /// <summary>
    /// 更新实体卡 ✏️
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("更新实体卡")]
    [ApiDescriptionSettings(Name = "Update"), HttpPost]
    public async Task Update(UpdateProductCardInput input)
    {
        if (await _productCardRep.IsAnyAsync(u => u.Id != input.Id && u.Code != null && u.Code == input.Code))
            throw Oops.Oh("ICCID已存在");

        var entity = input.Adapt<ProductCard>();
        await _productCardRep.AsUpdateable(entity).ExecuteCommandAsync();
    }
    #endregion

    #region 删除实体卡
    /// <summary>
    /// 删除实体卡 ❌
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("删除实体卡")]
    [ApiDescriptionSettings(Name = "Delete"), HttpPost]
    public async Task Delete(DeleteProductCardInput input)
    {
        var entity = await _productCardRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
        await this.BusinessLogService.Create($"删除实体卡：{string.Join(",", entity.Code)}");
        //await _productCardRep.FakeDeleteAsync(entity);   //假删除
        await _productCardRep.DeleteAsync(entity);   //真删除
    }
    #endregion

    #region 批量删除实体卡
    /// <summary>
    /// 批量删除实体卡 ❌
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("批量删除实体卡")]
    [ApiDescriptionSettings(Name = "BatchDelete"), HttpPost]
    public async Task<int> BatchDelete([Required(ErrorMessage = "主键列表不能为空")] List<DeleteProductCardInput> input)
    {
        var exp = Expressionable.Create<ProductCard>();
        foreach (var row in input)
            exp = exp.Or(it => it.Id == row.Id);
        var list = await _productCardRep.AsQueryable().Where(exp.ToExpression()).ToListAsync();
        var codeList = list.Select(x => x.Code).ToList();
        await this.BusinessLogService.Create($"批量删除实体卡：{string.Join(",", codeList)}");
        //return await _productCardRep.FakeDeleteAsync(list);   //假删除
        return await _productCardRep.DeleteAsync(list) ? list.Count : 0;   //真删除
    }
    #endregion

    #region 导出实体卡记录
    /// <summary>
    /// 导出实体卡记录 🔖
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("导出实体卡记录")]
    [ApiDescriptionSettings(Name = "Export"), HttpPost, NonUnify]
    public async Task<IActionResult> Export(PageProductCardInput input)
    {
        input.IncludePasscode = true;
        var configModel = await this._sqlSugarClient.Queryable<SysConfig>().Where(x => x.Code == ApplicationConst.Config_ProductCardActivateUrlForamt).FirstAsync();
        if (string.IsNullOrWhiteSpace(configModel?.Value))
            throw Oops.Oh(BusinessErrorCodeEnum.C0004);

        var modelList = (await this.Page(input)).Items;
        if (input.SelectKeyList?.Count > 0)
            modelList = modelList.Where(x => input.SelectKeyList.Contains(x.Id)).ToList();

        var statusDictMap = this._sysDictTypeService.GetDataList(new GetDataDictTypeInput { Code = "ProductCardStatusEnum" }).Result.ToDictionary(x => x.Value, x => x.Label);
        var productList = this.ProductService.DropdownData().Result;
        var buttons = await this._sysMenuService.GetOwnBtnPermList();
        if (buttons.Contains(Sinc.ERP.Core.SeedData.SysMenuSeedData.Btn_ProductCard_NonCustomerData))
        {
            var customerList = await this.CustomerService.DropdownData();
            var resultList = modelList.Adapt<List<ExportProductCardOutput>>();
            foreach (var result in resultList)
            {
                var model = modelList.FirstOrDefault(x => x.Id == result.Id);
                result.Code = model.FullCode;
                result.Url = string.Format(configModel.Value, model.FullCode, model.Passcode);

                statusDictMap.TryGetValue($"{(int)result.Status.Value}", out var statusLabel);
                result.StatusLabel = statusLabel;

                var product = productList.FirstOrDefault(x => x.Id == result.ProductId);
                result.ProductName = product?.Name;

                var customer = customerList.FirstOrDefault(x => x.Id == model.CustomerId);
                result.CustomerLabel = customer?.Name;
            }
            return this.ExportData(resultList, "实体卡导出记录");
        }
        else
        {
            var resultList = modelList.Adapt<List<CustomerExportOutput>>();
            foreach (var result in resultList)
            {
                var model = modelList.FirstOrDefault(x => x.Id == result.Id);
                result.Code = model.FullCode;
                result.Url = string.Format(configModel.Value, model.FullCode, model.Passcode);

                statusDictMap.TryGetValue($"{(int)result.Status.Value}", out var statusLabel);
                result.StatusLabel = statusLabel;

                var product = productList.FirstOrDefault(x => x.Id == result.ProductId);
                result.ProductName = product?.Name;
            }
            return this.ExportData(resultList, "实体卡导出记录");
        }
    }
    #endregion

    #region 下载实体卡数据导入模板
    /// <summary>
    /// 下载实体卡数据导入模板 ⬇️
    /// </summary>
    /// <returns></returns>
    [DisplayName("下载实体卡数据导入模板")]
    [ApiDescriptionSettings(Name = "Import"), HttpGet, NonUnify]
    public IActionResult DownloadTemplate()
    {
        return this.ExportData(new List<ExportProductCardOutput>(), "实体卡导入模板");
    }

    private IActionResult ExportData(List<ExportProductCardOutput> list, string fileName)
    {
        var productList = this.ProductService.DropdownData().Result;
        var stream = ExcelHelper.ExportTemplate(list, fileName, (worksheet, prop) =>
        {
            if (prop.Name == nameof(ExportProductCardOutput.ProductName))
            {
                return productList.Count == 0 ? null : productList.Select(x => x.Name).ToList();
            }
            return null;
        });
        return stream;
    }

    private IActionResult ExportData(List<CustomerExportOutput> list, string fileName)
    {
        var productList = this.ProductService.DropdownData().Result;
        var stream = ExcelHelper.ExportTemplate(list, fileName, (worksheet, prop) =>
        {
            if (prop.Name == nameof(ExportProductCardOutput.ProductName))
            {
                return productList.Count == 0 ? null : productList.Select(x => x.Name).ToList();
            }
            return null;
        });
        return stream;
    }
    #endregion

    #region 导入实体卡记录
    /// <summary>
    /// 导入实体卡记录 💾
    /// </summary>
    /// <returns></returns>
    //[DisplayName("导入实体卡记录")]
    //[ApiDescriptionSettings(Name = "Import"), HttpPost, NonUnify, UnitOfWork]
    //public IActionResult ImportData([Required] IFormFile file)
    //{
    //    lock (this)
    //    {
    //        var batchNumber = this.SequenceInternalService.GetNextSequenceValue(ApplicationConst.Sequence_ProductCardImportBatchNumber);
    //        var productList = this.ProductService.DropdownData().Result;
    //        var statusDictMap = _sysDictTypeService.GetDataList(new GetDataDictTypeInput { Code = "ProductCardStatusEnum" }).Result.ToDictionary(x => x.Label!, x => x.Value);
    //        var stream = ExcelHelper.ImportData<ImportProductCardInput, ProductCard>(file, (list, markerErrorAction) =>
    //        {
    //            _sqlSugarClient.Utilities.PageEach(list, 2048, pageItems =>
    //            {
    //                // 校验并过滤必填基本类型为null的字段
    //                var rows = pageItems.Where(x =>
    //                {
    //                    var product = productList.FirstOrDefault(p => p.Name == x.ProductName);
    //                    if (product == null)
    //                    {
    //                        x.Error = "产品名称无效";
    //                        return false;
    //                    }
    //                    x.ProductId = product.Id;

    //                    if (!Enum.TryParse(statusDictMap.GetValueOrDefault(x.StatusLabel), out ProductCardStatusEnum status))
    //                    {
    //                        x.Error = "状态无效";
    //                        return false;
    //                    }
    //                    x.Status = status;
    //                    x.BatchNumber = batchNumber;
    //                    return true;
    //                }).Adapt<List<ProductCard>>();

    //                var storageable = _productCardRep.Context.Storageable(rows)
    //                    .SplitError(it => string.IsNullOrWhiteSpace(it.Item.Code), "ICCID不能为空")
    //                    .SplitError(it => it.Item.Code?.Length > 100, "ICCID长度不能超过100个字符")
    //                    .SplitError(it => it.Item.Passcode?.Length > 50, "校验码长度不能超过50个字符")
    //                    .WhereColumns(it => new { it.BatchNumber }).SplitError(it => it.Any(), "导入批次号已存在")
    //                    .WhereColumns(it => new { it.Code }).SplitError(it => it.Any(), "ICCID已存在")
    //                    .SplitInsert(_ => true)
    //                    .ToStorage();

    //                storageable.BulkCopy();
    //                storageable.BulkUpdate();

    //                // 标记错误信息
    //                markerErrorAction.Invoke(storageable, pageItems, rows);
    //            });
    //        });

    //        return stream;
    //    }
    //}
    #endregion

    #region 设置使用状态
    /// <summary>
    /// 设置使用状态
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("设置使用状态")]
    [ApiDescriptionSettings(Name = "SetStatus"), HttpPost, NonUnify, UnitOfWork]
    public async Task SetStatus(SetStatusProductCardInput input)
    {
        if (input == null || input.IdList.IsEmpty())
            throw Oops.Oh(ErrorCodeEnum.D1002);

        var modelList = await this._productCardRep.AsQueryable().Where(x => input.IdList.Contains(x.Id)).ToListAsync();
        if (modelList.Count == 0)
            throw Oops.Oh(ErrorCodeEnum.D1002);

        if (modelList.Any(x => x.Status == ProductCardStatusEnum.Created))
            throw Oops.Oh("待生效状态不能设置");

        modelList = modelList.Where(x => x.Status != input.Status).ToList();
        modelList.ForEach(x => x.Status = input.Status);
        await this._productCardRep.AsUpdateable(modelList).UpdateColumns(x => new { x.Status, x.UpdateUserId, x.UpdateUserName, x.UpdateTime }).ExecuteCommandAsync();

        var statusList = await _sysDictTypeService.GetDataList(new GetDataDictTypeInput { Code = "ProductCardStatusEnum" });
        var statusDictMap = statusList.ToDictionary(x => x.Value, x => x.Label);
        if (!statusDictMap.TryGetValue($"{(int)input.Status}", out var statusLabel))
            statusLabel = "未知状态";
        await this.BusinessLogService.Create($"批量设置实体卡为{statusLabel}：{string.Join(",", modelList.Select(x => x.Code))}");
    }
    #endregion

    #region 复制链接并设置为已使用
    /// <summary>
    /// 复制链接并设置为已使用 ℹ️
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("复制链接并设置为已使用")]
    [ApiDescriptionSettings(Name = "CopyLink"), HttpPost]
    public async Task<string> CopyLink(QueryByIdProductCardInput input)
    {
        var configModel = await this._sqlSugarClient.Queryable<SysConfig>().Where(x => x.Code == ApplicationConst.Config_ProductCardActivateUrlForamt).FirstAsync();
        if (string.IsNullOrWhiteSpace(configModel?.Value))
            throw Oops.Oh(BusinessErrorCodeEnum.C0004);

        var model = await _productCardRep.GetFirstAsync(u => u.Id == input.Id);
        if (model == null)
            throw Oops.Oh(ErrorCodeEnum.D1002);

        if (model.Status == ProductCardStatusEnum.Created)
            throw Oops.Oh("待生效状态不能复制");

        if (model.Status == ProductCardStatusEnum.Available)
        {
            model.Status = ProductCardStatusEnum.Used;
            await this._productCardRep.AsUpdateable(model).UpdateColumns(x => new { x.Status, x.UpdateUserId, x.UpdateUserName, x.UpdateTime }).ExecuteCommandAsync();
        }
        await this.BusinessLogService.Create($"[{this._userManager.RealName}]复制实体卡链接：{model.Code}");
        return string.Format(configModel.Value, model.FullCode, model.Passcode);
    }
    #endregion

    #region 激活实体卡
    /// <summary>
    /// 激活实体卡
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("激活实体卡")]
    [ApiDescriptionSettings(Name = "Activate"), HttpPost]
    public async Task<ActivateProductCardOutput> ActivateCard(SetStatusProductCardInput input)
    {
        if (input == null || input.IdList.IsEmpty())
            throw Oops.Oh(ErrorCodeEnum.D1002);

        var activateKeyModel = await this._sqlSugarClient.Queryable<SysConfig>().Where(x => x.Code == ApplicationConst.Config_ProductCardActivateKey).FirstAsync();
        if (string.IsNullOrWhiteSpace(activateKeyModel?.Value))
            throw Oops.Oh(BusinessErrorCodeEnum.C0002);

        var modelList = await this._productCardRep.AsQueryable().Where(x => input.IdList.Contains(x.Id)).ToListAsync();
        if (modelList.IsEmpty())
            throw Oops.Oh(ErrorCodeEnum.D1002);

        if (modelList.Any(x => x.Status != ProductCardStatusEnum.Available))
            throw Oops.Oh(BusinessErrorCodeEnum.C0001);

        if (modelList.Any(x => x.FullCode.IsEmpty() || x.Passcode.IsEmpty()))
            throw Oops.Oh(BusinessErrorCodeEnum.C0003);

        var output = new ActivateProductCardOutput();
        var chineMobileService = new ChineMobileService(activateKeyModel.Value);
        foreach (var model in modelList)
        {
            var (isSuccess, message) = await chineMobileService.Activate(model.FullCode, model.Passcode);
            output.AddResult(model.Code, isSuccess, message);
            if (!isSuccess)
            {
                await this.BusinessLogService.Create($"激活实体卡失败：{model.Code}，原因：{message}");
                continue;
            }

            output.SuccessCount++;
            model.Status = ProductCardStatusEnum.Used;
            await this._productCardRep.UpdateAsync(model);
            await this.BusinessLogService.Create($"激活实体卡成功：{model.Code}");
        }
        return output;
    }
    #endregion
}
